package com.neborosoft.lib;

import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: CM
 * Date: 08.01.13
 * Time: 0:05
 * To change this template use File | Settings | File Templates.
 */
public final class Maps {
    public static <K,V> Map<K,V> accessOrderMap(){
        return new LinkedHashMap<K, V>(100,0.75f,true);
    }

    public static interface OnRemoveListener<K,V>{
        void onRemove(K key, V value);
    }

    public static <K> Map<K,Long> cachedBySizeMap(final long maxSize,
                                                  final OnRemoveListener<K,Long> onRemoveListener)
    {
        return new LinkedHashMap<K, Long>(100,0.75f,true){
            private int currentSize = 0;

            @Override
            public Long put(K key, Long value) {
                if(value > maxSize){
                    return value;
                }

                currentSize += value;
                Iterator<Entry<K,Long>> iterator = entrySet().iterator();
                while (currentSize > maxSize && iterator.hasNext()){
                    Entry<K,Long> pair = iterator.next();
                    iterator.remove();

                    if(onRemoveListener != null){
                        onRemoveListener.onRemove(pair.getKey(), pair.getValue());
                    }
                }

                return super.put(key,value);
            }
        };
    }

    public static <K> Map<K,Long> cachedBySizeMap(final long maxSize){
        return cachedBySizeMap(maxSize, null);
    }

    public static <K,V> V getWithOptional(Map<K,V> map, K key, V defaultValue){
        V value = map.get(key);
        if(value == null){
            return defaultValue;
        } else {
            return value;
        }
    }

    public static <K> Integer translateMapValue(Map<K,Integer> map, K key, int offset){
        Integer value = map.get(key);
        if(value == null){
            return null;
        }

        map.put(key, value + offset);
        return value;
    }

    public static <K> void incrementMapValue(Map<K,Integer> map, K key){
        if(translateMapValue(map, key, 1) == null){
            map.put(key,1);
        }
    }

    public static <K> void decrementMapValue(Map<K,Integer> map, K key){
        if(translateMapValue(map, key, -1) == null){
            map.put(key,1);
        }
    }

    public static <K> void decrementMapUnsignedValue(Map<K,Integer> map, K key){
        Integer prevValue = translateMapValue(map, key, -1);
        if(prevValue == null){
            return;
        }

        if(prevValue.equals(1)){
            map.remove(key);
        }
    }

    public static Object[] toArray(Map map){
        return map.entrySet().toArray();
    }

    public static <K,V extends Comparable<V>> Object[]
        getSortedByValueMapArrayRepresentation(Map<K,V> map)
    {
        Object[] result = toArray(map);
        Arrays.sort(result, new Comparator() {
            @Override
            public int compare(Object a, Object b) {
                return ((Map.Entry<K,V>)a).getValue().compareTo(((Map.Entry<K,V>)b).getValue());
            }
        });

        return result;
    }

    public static Map arrayToDuplicatedKeysMap(final Object[][] array){
        return new AbstractMap() {
            @Override
            public Set entrySet() {
                return new AbstractSet() {

                    @Override
                    public Iterator iterator() {
                        return new Iterator() {
                            int index = 0;

                            @Override
                            public boolean hasNext() {
                                return index < array.length;
                            }

                            @Override
                            public Object next() {
                                return array[index++];
                            }

                            @Override
                            public void remove() {
                                throw new UnsupportedOperationException();
                            }
                        };
                    }

                    @Override
                    public int size() {
                        return array.length;
                    }

                };
            }
        };
    }
}
